<!DOCTYPE html>
<html class="writer-html5" lang="zh" >
<head>
  <meta charset="utf-8" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />

  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Ch1-2 初始化流程 &mdash; EasyVulkan</title>
      <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
      <link rel="stylesheet" href="_static/css/theme.css" type="text/css" />
      <link rel="stylesheet" href="_static/theme.css" type="text/css" />
  <!--[if lt IE 9]>
    <script src="_static/js/html5shiv.min.js"></script>
  <![endif]-->
  
        <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
        <script src="_static/jquery.js"></script>
        <script src="_static/underscore.js"></script>
        <script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
        <script src="_static/doctools.js"></script>
    <script src="_static/js/theme.js"></script>
    <link rel="index" title="索引" href="genindex.html" />
    <link rel="search" title="搜索" href="search.html" />
    <link rel="next" title="Ch1-3 创建VK实例与逻辑设备" href="Ch1-3%20%E5%88%9B%E5%BB%BAVK%E5%AE%9E%E4%BE%8B%E4%B8%8E%E9%80%BB%E8%BE%91%E8%AE%BE%E5%A4%87.html" />
    <link rel="prev" title="Ch1-1 创建GLFW窗口" href="Ch1-1%20%E5%88%9B%E5%BB%BAGLFW%E7%AA%97%E5%8F%A3.html" /> 
</head>

<body class="wy-body-for-nav"> 
  <div class="wy-grid-for-nav">
    <nav data-toggle="wy-nav-shift" class="wy-nav-side">
      <div class="wy-side-scroll">
        <div class="wy-side-nav-search" >
            <a href="index.html" class="icon icon-home"> EasyVulkan
            <img src="_static/logo1.png" class="logo" alt="Logo"/>
          </a>
<div role="search">
  <form id="rtd-search-form" class="wy-form" action="search.html" method="get">
    <input type="text" name="q" placeholder="在文档中搜索" />
    <input type="hidden" name="check_keywords" value="yes" />
    <input type="hidden" name="area" value="default" />
  </form>
</div>
        </div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
              <p class="caption" role="heading"><span class="caption-text">第一章 初始化</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="Ch1-0%20%E5%87%86%E5%A4%87%E5%B7%A5%E4%BD%9C.html">Ch1-0 准备工作</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch1-1%20%E5%88%9B%E5%BB%BAGLFW%E7%AA%97%E5%8F%A3.html">Ch1-1 创建GLFW窗口</a></li>
<li class="toctree-l1 current"><a class="current reference internal" href="#">Ch1-2 初始化流程</a><ul>
<li class="toctree-l2"><a class="reference internal" href="#id1">单例类</a></li>
<li class="toctree-l2"><a class="reference internal" href="#id2">初始化流程</a><ul>
<li class="toctree-l3"><a class="reference internal" href="#vulkan">创建Vulkan实例的步骤</a></li>
<li class="toctree-l3"><a class="reference internal" href="#debug-messenger">创建debug messenger的步骤</a></li>
<li class="toctree-l3"><a class="reference internal" href="#window-surafce">创建window surafce的步骤</a></li>
<li class="toctree-l3"><a class="reference internal" href="#id3">创建逻辑设备的步骤</a></li>
<li class="toctree-l3"><a class="reference internal" href="#id4">创建交换链的步骤</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="#id5">若要使用最新版Vulkan</a></li>
<li class="toctree-l2"><a class="reference internal" href="#id6">代码汇总</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="Ch1-3%20%E5%88%9B%E5%BB%BAVK%E5%AE%9E%E4%BE%8B%E4%B8%8E%E9%80%BB%E8%BE%91%E8%AE%BE%E5%A4%87.html">Ch1-3 创建VK实例与逻辑设备</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch1-4%20%E5%88%9B%E5%BB%BA%E4%BA%A4%E6%8D%A2%E9%93%BE.html">Ch1-4 创建交换链</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">第二章 绘制一个三角形</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="Ch2-0%20%E4%BB%A3%E7%A0%81%E6%95%B4%E7%90%86%E5%8F%8A%E4%B8%80%E4%BA%9B%E8%BE%85%E5%8A%A9%E7%B1%BB.html">Ch2-0 代码整理及一些辅助类</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch2-1%20Rendering%20Loop.html">Ch2-1 Rendering Loop</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch2-2%20%E5%88%9B%E5%BB%BA%E6%B8%B2%E6%9F%93%E9%80%9A%E9%81%93%E5%92%8C%E5%B8%A7%E7%BC%93%E5%86%B2.html">Ch2-2 创建渲染通道和帧缓冲</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch2-3%20%E5%88%9B%E5%BB%BA%E7%AE%A1%E7%BA%BF%E5%B9%B6%E7%BB%98%E5%88%B6%E4%B8%89%E8%A7%92%E5%BD%A2.html">Ch2-3 创建管线并绘制三角形</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">第三章 纵观Vulkan</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="Ch3-1%20%E5%90%8C%E6%AD%A5%E5%8E%9F%E8%AF%AD.html">Ch3-1 同步原语</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch3-2%20%E5%9B%BE%E5%83%8F%E4%B8%8E%E7%BC%93%E5%86%B2%E5%8C%BA.html">Ch3-2 图像与缓冲区</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch3-3%20%E7%AE%A1%E7%BA%BF%E5%B8%83%E5%B1%80%E5%92%8C%E7%AE%A1%E7%BA%BF.html">Ch3-3 管线布局和管线</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch3-4%20%E6%B8%B2%E6%9F%93%E9%80%9A%E9%81%93%E5%92%8C%E5%B8%A7%E7%BC%93%E5%86%B2.html">Ch3-4 渲染通道和帧缓冲</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch3-5%20%E5%91%BD%E4%BB%A4%E7%BC%93%E5%86%B2%E5%8C%BA.html">Ch3-5 命令缓冲区</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch3-6%20%E6%8F%8F%E8%BF%B0%E7%AC%A6.html">Ch3-6 描述符</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch3-7%20%E9%87%87%E6%A0%B7%E5%99%A8.html">Ch3-7 采样器</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch3-8%20%E6%9F%A5%E8%AF%A2.html">Ch3-8 查询</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">第四章 着色器</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="Ch4-1%20%E7%9D%80%E8%89%B2%E5%99%A8%E6%A8%A1%E7%BB%84.html">Ch4-1 着色器模组</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch4-2%20%E9%A1%B6%E7%82%B9%E7%9D%80%E8%89%B2%E5%99%A8.html">Ch4-2 顶点着色器</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch4-3%20%E7%89%87%E6%AE%B5%E7%9D%80%E8%89%B2%E5%99%A8.html">Ch4-3 片段着色器</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">第五章 封装常用对象</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="Ch5-0%20VKBase%2B.h.html">Ch5-0 VKBase+.h</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch5-1%20%E5%90%84%E7%A7%8D%E7%BC%93%E5%86%B2%E5%8C%BA.html">Ch5-1 各种缓冲区</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch5-2%202D%E8%B4%B4%E5%9B%BE%E5%8F%8A%E7%94%9F%E6%88%90Mipmap.html">Ch5-2 2D贴图及生成Mipmap</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch5-3%202D%E8%B4%B4%E5%9B%BE%E6%95%B0%E7%BB%84.html">Ch5-3 2D贴图数组</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">第六章 进阶Vulkan</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="Ch6-0%20%E4%BD%BF%E7%94%A8%E6%96%B0%E7%89%88%E6%9C%AC%E7%89%B9%E6%80%A7.html">Ch6-0 使用新版本特性</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch6-1%20%E6%97%A0%E5%9B%BE%E5%83%8F%E5%B8%A7%E7%BC%93%E5%86%B2.html">Ch6-1 无图像帧缓冲</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch6-2%20%E5%8A%A8%E6%80%81%E6%B8%B2%E6%9F%93.html">Ch6-2 动态渲染</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">第七章 基础示例</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="Ch7-1%20%E5%88%9D%E8%AF%86%E9%A1%B6%E7%82%B9%E7%BC%93%E5%86%B2%E5%8C%BA.html">Ch7-1 初识顶点缓冲区</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch7-2%20%E5%88%9D%E8%AF%86%E7%B4%A2%E5%BC%95%E7%BC%93%E5%86%B2%E5%8C%BA.html">Ch7-2 初识索引缓冲区</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch7-3%20%E5%88%9D%E8%AF%86%E5%AE%9E%E4%BE%8B%E5%8C%96%E7%BB%98%E5%88%B6.html">Ch7-3 初识实例化绘制</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch7-4%20%E5%88%9D%E8%AF%86Push%20Constant.html">Ch7-4 初识Push Constant</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch7-5%20%E5%88%9D%E8%AF%86Uniform%E7%BC%93%E5%86%B2%E5%8C%BA.html">Ch7-5 初识Uniform缓冲区</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch7-6%20%E6%8B%B7%E8%B4%9D%E5%9B%BE%E5%83%8F%E5%88%B0%E5%B1%8F%E5%B9%95.html">Ch7-6 拷贝图像到屏幕</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch7-7%20%E4%BD%BF%E7%94%A8%E8%B4%B4%E5%9B%BE.html">Ch7-7 使用贴图</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">第八章 简单示例</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="Ch8-1%20%E7%A6%BB%E5%B1%8F%E6%B8%B2%E6%9F%93.html">Ch8-1 离屏渲染</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch8-2%20%E6%B7%B1%E5%BA%A6%E6%B5%8B%E8%AF%95%E5%92%8C%E6%B7%B1%E5%BA%A6%E5%8F%AF%E8%A7%86%E5%8C%96.html">Ch8-2 深度测试和深度可视化</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch8-3%20%E5%BB%B6%E8%BF%9F%E6%B8%B2%E6%9F%93.html">Ch8-3 延迟渲染</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch8-4%20%E9%A2%84%E4%B9%98Alpha.html">Ch8-4 预乘Alpha</a></li>
<li class="toctree-l1"><a class="reference internal" href="Ch8-5%20sRGB%E8%89%B2%E5%BD%A9%E7%A9%BA%E9%97%B4%E4%B8%8E%E5%BC%80%E5%90%AFHDR.html">Ch8-5 sRGB色彩空间与开启HDR</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">附录</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="Ap1-1%20%E8%BF%90%E8%A1%8C%E6%9C%9F%E7%BC%96%E8%AF%91GLSL.html">Ap1-1 运行期编译GLSL</a></li>
</ul>

        </div>
      </div>
    </nav>

    <section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Mobile navigation menu" >
          <i data-toggle="wy-nav-top" class="fa fa-bars"></i>
          <a href="index.html">EasyVulkan</a>
      </nav>

      <div class="wy-nav-content">
        <div class="rst-content">
          <div role="navigation" aria-label="Page navigation">
  <ul class="wy-breadcrumbs">
      <li><a href="index.html" class="icon icon-home"></a> &raquo;</li>
      <li>Ch1-2 初始化流程</li>
      <li class="wy-breadcrumbs-aside">
      </li>
  </ul>
  <hr/>
</div>
          <div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
           <div itemprop="articleBody">
             
  <section id="ch1-2">
<h1>Ch1-2 初始化流程<a class="headerlink" href="#ch1-2" title="Permalink to this heading"></a></h1>
<p>
    为什么第一章的标题不是“从零到三角形”？因为步骤太多了！所以我分了两章来讲。
    <br>
    初始化一个Vulkan应用程序所必备的步骤已足以算得上繁多，在这一节先对所需步骤进行总结，解释名词概念，并定义各步骤中将要书写的函数及自建类型之名称。
</p><section id="id1">
<h2>单例类<a class="headerlink" href="#id1" title="Permalink to this heading"></a></h2>
<p>
    之前在<span class="path">VKBase.h</span>中添加了一个叫vulkan的命名空间，现在该命名空间中定义一个单例类<span class="type">graphicsBase</span>，该类将被用于管理Vulkan中那些最基础的对象和行为。
</p>
<pre class="code">
<span class="kw">class</span> <span class="type">graphicsBase</span> {
    <span class="cmt">//静态变量</span>
    <span class="kw">static</span> <span class="type">graphicsBase</span> singleton;
    <span class="cmt">//--------------------</span>
    <span class="fn">graphicsBase</span>() = <span class="kw">default</span>;
    <span class="fn">graphicsBase</span>(<span class="type">graphicsBase</span>&amp;&amp;) = <span class="kw">delete</span>;
    <span class="fn">~graphicsBase</span>() {
        <span class="cmt">/*待Ch1-4填充*/</span>
    }
<span class="kw">public</span>:
    <span class="cmt">//静态函数</span>
    <span class="cmt">//该函数用于访问单例</span>
    <span class="kw">static</span> <span class="type">graphicsBase</span>&amp; <span class="sfn">Base</span>() {
        <span class="kw">return</span> singleton;
    }
};
<span class="kw">inline</span> <span class="type">graphicsBase</span> <span class="type">graphicsBase</span>::singleton;
</pre>
<ul>
    <li>
        <p>
            定义移动构造器（删除也算进行了定义），且没有定义复制构造器、复制赋值、移动赋值时，上述四个函数将全部无法使用，由此构成单例类。
        </p>
    </li>
</ul></section>
<section id="id2">
<h2>初始化流程<a class="headerlink" href="#id2" title="Permalink to this heading"></a></h2>
<p>
    一个呈现图像的Vulkan应用程序需经历以下的步骤以初始化：
    <br>
    1.创建Vulkan实例
    <br>
    2.创建debug messenger（若编译选项为DEBUG）
    <br>
    3.创建window surface
    <br>
    4.选择物理设备并创建逻辑设备，取得队列
    <br>
    5.创建交换链
</p>
<p>
    <strong>什么是Vulkan实例？</strong>
    <br>
    应用程序必须显式地告诉操作系统，说明其需要使用Vulkan的功能，这一步是由创建Vulkan实例（<span class="type">VkInstance</span>）来完成的。
    <br>
    Vulkan实例的底层是一系列Vulkan运行所必需的用于记录状态和信息的变量。
</p>
<p>
    <strong>什么是debug messenger？</strong>
    <br>
    Debug messenger用于获取验证层所捕捉到的debug信息。若没有这东西，Vulkan编程可谓寸步难行。
</p>
<p>
    <strong>什么是window surface？</strong>
    <br>
    Vulkan是平台无关的API，你必须向其提供一个window surface（<span class="type">VkSurfaceKHR</span>），以和平台特定的窗口对接。
</p>
<p>
    <strong>什么是物理设备和逻辑设备？</strong>
    <br>
    物理设备即图形处理器，通常为GPU。Vulkan中所谓的物理设备虽称物理（physical），但并非必须是实体设备。
    <br>
    <span class="type">VkPhysicalDevice</span>类型指代物理设备，从这类handle只能获取物理设备信息。<span class="type">VkDevice</span>类型是逻辑设备的handle，逻辑设备是编程层面上用来与物理设备交互的对象，关于分配设备内存、创建Vulkan相关对象的命令大都会被提交给逻辑设备。
</p>
<p>
    <strong>什么是队列？</strong>
    <br>
    队列（<span class="type">VkQueue</span>）类似于线程，命令被提交到队列执行，<a href="https://renderdoc.org/vkspec_chunked/chap3.html#fundamentals-queueoperation">Vulkan官方标准</a>中将队列描述为命令执行引擎的接口：
    <br>
    <span class="ref">Vulkan queues provide an interface to the execution engines of a device.</span>
    <br>
    Vulkan核心功能中规定队列支持的操作类型包括图形、计算、数据传送、稀疏绑定四种，图形和计算队列必定也支持数据传送。一族功能相同的队列称为队列族。
    <br>
    任何支持Vulkan的显卡驱动确保你能找到<strong>至少一个</strong>同时支持图形和计算操作的队列族。
</p>
<p>
    <strong>什么是交换链？</strong>
    <br>
    在将一张图像用于渲染或其他类型的写入时，已渲染好的图像可以被呈现引擎读取，如此交替呈现在窗口中的数张图像的集合即为交换链。
</p><section id="vulkan">
<h3>创建Vulkan实例的步骤<a class="headerlink" href="#vulkan" title="Permalink to this heading"></a></h3>
<p>
    创建Vulkan实例的步骤依序为：
    <br>
    1.确定所需的实例级别层及扩展，不检查是否可用
    <br>
    2.用<a href="https://renderdoc.org/vkspec_chunked/chap4.html#vkCreateInstance">vkCreateInstance</a>(...)创建Vulkan实例
</p>
<p>
    <strong>什么是层和扩展？</strong>
    <br>
    层和扩展在Vulkan的核心功能之外，提供了对特定需求、特定平台的支持，还包括特定厂商提供的功能。“扩展”一词顾名思义，而层与扩展的主要区别在于，层有显著的作用范围，比如验证层会检查几乎所有Vulkan相关的函数。此外，有些扩展需要特定的层才能使用。
    <br>
    层只有实例级别的（Vulkan1.0中定义过设备级别的层，但这一概念之后被废弃，Vulkan1.0版本中亦不存在设备级别独占的层），而扩展则分实例级别和设备级别，前者与特定设备无关，可能作用于所有Vulkan相关的内容，而后者则作用于与特定设备相关的内容。
</p>
<p>
    <strong>为什么不在创建Vulkan实例前检查层和扩展的可用性？</strong>
    <br>
    如果你读过一些其他的Vulkan教程，你可能有读到在创建Vulkan实例前，会先检查层和扩展是否可用，我选择跳过这一步。
    <br>
    <a href="https://renderdoc.org/vkspec_chunked/chap4.html#vkCreateInstance">vkCreateInstance</a>(...)在创建Vulkan实例时本就会检查层和扩展的可用性，该函数在无法满足所需层时返回<span class="enum">VK_ERROR_LAYER_NOT_PRESENT</span>，在无法满足所需扩展时返回<span class="enum">VK_ERROR_EXTENSION_NOT_PRESENT</span>。因此，我建议根据<a href="https://renderdoc.org/vkspec_chunked/chap4.html#vkCreateInstance">vkCreateInstance</a>(...)的返回值，仅在创建Vulkan实例失败时检查层和扩展的可用性。
</p>
<p>
    于是在<span class="type">graphicsBase</span>中加入以下内容：
</p>
<pre class="code">
<span class="kw">private</span>:
<span class="cmt">//单例类对象是静态的，未设定初始值亦无构造函数的成员会被零初始化</span>
<span class="type">VkInstance</span> instance;
std::<span class="type">vector</span>&lt;<span class="kw">const char</span>*&gt; instanceLayers;
std::<span class="type">vector</span>&lt;<span class="kw">const char</span>*&gt; instanceExtensions;

<span class="cmt">//该函数用于向instanceLayers或instanceExtensions容器中添加字符串指针，并确保不重复</span>
<span class="kw">static void</span> <span class="sfn">AddLayerOrExtension</span>( std::<span class="type">vector</span>&lt;<span class="kw">const char</span>*&gt;&amp; container, <span class="kw">const char</span>* name) {
    <span class="kw">for</span> (<span class="kw">auto</span>&amp; i : container)
        <span class="kw">if</span> (!<span class="fn">strcmp</span>(name, i))<span class="cmt">//strcmp(...)在字符串匹配时返回0</span>
            <span class="kw">return</span>;          <span class="cmt">//如果层/扩展的名称已在容器中，直接返回</span>
    container.<span class="fn">push_back</span>(name);
}
<span class="kw">public</span>:
<span class="cmt">//Getter</span>
<span class="type">VkInstance</span> <span class="fn">Instance</span>() <span class="kw">const</span> {
    <span class="kw">return</span> instance;
}
<span class="kw">const</span> std::<span class="type">vector</span>&lt;<span class="kw">const char</span>*&gt;&amp; <span class="fn">InstanceLayers()</span> <span class="kw">const</span> {
    <span class="kw">return</span> instanceLayers;
}
<span class="kw">const</span> std::<span class="type">vector</span>&lt;<span class="kw">const char</span>*&gt;&amp; <span class="fn">InstanceExtensions()</span> <span class="kw">const</span> {
    <span class="kw">return</span> instanceExtensions;
}

<span class="cmt">//以下函数用于创建Vulkan实例前</span>
<span class="kw">void</span> <span class="fn">AddInstanceLayer</span>(<span class="kw">const char</span>* layerName) {
    <span class="sfn">AddLayerOrExtension</span>(instanceLayers, layerName);
}
<span class="kw">void</span> <span class="fn">AddInstanceExtension</span>(<span class="kw">const char</span>* extensionName) {
    <span class="sfn">AddLayerOrExtension</span>(instanceExtensions, extensionName);
}
<span class="cmt">//该函数用于创建Vulkan实例</span>
<span class="type">VkResult</span> <span class="fn">CreateInstance</span>(<span class="type">VkInstanceCreateFlags</span> flags = 0) {
    <span class="cmt">/*待Ch1-3填充*/</span>
}
<span class="cmt">//以下函数用于创建Vulkan实例失败后</span>
<span class="type">VkResult</span> <span class="fn">CheckInstanceLayers</span>(std::<span class="type">span</span>&lt;<span class="kw">const char</span>*&gt; layersToCheck) {
    <span class="cmt">/*待Ch1-3填充*/</span>
}
<span class="kw">void</span> <span class="fn">InstanceLayers</span>(<span class="kw">const</span> std::<span class="type">vector</span>&lt;<span class="kw">const char</span>*&gt;&amp; layerNames) {
    instanceLayers = layerNames;
}
<span class="type">VkResult</span> <span class="fn">CheckInstanceExtensions</span>(std::<span class="type">span</span>&lt;<span class="kw">const char</span>*&gt; extensionsToCheck, <span class="kw">const char</span>* layerName = <span class="kw">nullptr</span>) <span class="kw">const</span> {
    <span class="cmt">/*待Ch1-3填充*/</span>
}
<span class="kw">void</span> <span class="fn">InstanceExtensions</span>(<span class="kw">const</span> std::<span class="type">vector</span>&lt;<span class="kw">const char</span>*&gt;&amp; extensionNames) {
    instanceExtensions = extensionNames;
}
</pre>
<ul>
    <li>
        <p>
            各种<span class="var">flags</span>会之后在<a class="reference internal" href="Ch1-3%20%E5%88%9B%E5%BB%BAVK%E5%AE%9E%E4%BE%8B%E4%B8%8E%E9%80%BB%E8%BE%91%E8%AE%BE%E5%A4%87.html#id2">Ch1-3</a>再做说明。
        </p>
    </li>
    <li>
        <p>
            我打算让<span class="fn">CheckInstanceLayers</span>(...)和<span class="fn">CheckInstanceExtensions</span>(...)将传入的<span class="type">span</span>中不可用的层和扩展设置为<span class="kw">nullptr</span>，然后与原本保存的<span class="var">instanceLayers</span>和<span class="var">instanceExtensions</span>比对，即可确定哪些层和扩展不可用。返回值则指示在获取可用层或扩展列表时是否发生错误。
        </p>
    </li>
</ul>
<p>
    <strong>于是剧本流程是这样的：</strong>
    <br>
    1.在创建Vulkan实例前，用<span class="fn">AddInstanceLayer</span>(...)和<span class="fn">AddInstanceExtension</span>(...)向对应的<span class="type">vector</span>中添加指向层和扩展名称的指针。
    <br>
    2.然后尝试用<span class="fn">CreateInstance</span>(...)创建Vulkan实例。
    <br>
    3.若创建Vulkan实例失败，若<a href="https://renderdoc.org/vkspec_chunked/chap4.html#vkCreateInstance">vkCreateInstance</a>(...)返回<span class="enum">VK_ERROR_LAYER_NOT_PRESENT</span>，从<span class="fn">InstanceLayers</span>()复制一份<span class="var">instanceLayers</span>，用<span class="fn">CheckInstanceLayers</span>(...)检查可用性，若不可用的仅为非必要的层，创建一份去除该层后的<span class="type">vector</span>，用<span class="type">InstanceLayers</span>(...)复制给<span class="var">instanceLayers</span>。返回<span class="enum">VK_ERROR_EXTENSION_NOT_PRESENT</span>的情况亦类似。然后重新尝试创建Vulkan实例。
</p></section>
<section id="debug-messenger">
<h3>创建debug messenger的步骤<a class="headerlink" href="#debug-messenger" title="Permalink to this heading"></a></h3>
<p>
    创建了Vulkan实例后，即可创建debug messenger，以便检查初始化流程中的所有其他步骤。
    <br>
    创建debug messenger可以粗略地说是只有一步，在<span class="type">graphicsBase</span>中加入以下内容：
</p>
<pre class="code">
<span class="kw">private</span>:
<span class="type">VkDebugUtilsMessengerEXT</span> debugMessenger;
<span class="cmt">//以下函数用于创建debug messenger</span>
<span class="type">VkResult</span> <span class="fn">CreateDebugMessenger</span>() {
    <span class="cmt">/*待Ch1-3填充*/</span>
}
</pre></section>
<section id="window-surafce">
<h3>创建window surafce的步骤<a class="headerlink" href="#window-surafce" title="Permalink to this heading"></a></h3>
<p>
    在创建了glfw窗口和Vulkan实例后，用<a href="https://www.glfw.org/docs/3.3/group__vulkan.html#ga1a24536bec3f80b08ead18e28e6ae965">glfwCreateWindowSurface</a>(...)创建window surface。这一步会写在之前的<span class="type">InitializeWindow</span>(...)中。
    <br>
    如果你是自行用WindowsAPI创建的窗口，用<a href="https://renderdoc.org/vkspec_chunked/chap34.html#vkCreateWin32SurfaceKHR">vkCreateWin32SurfaceKHR</a>(...)来创建窗口window surface，具体参考示例代码中的<a href="https://github.com/EasyVulkan/EasyVulkan.github.io/blob/main/solution/EasyVulkan_Ch8/WinGeneral.hpp">WinGeneral.hpp</a>。
</p>
<p>
    创建了window surface后当然需要将其存起来以备使用，于是在<span class="type">graphicsBase</span>中加入以下内容：
</p>
<pre class="code">
<span class="kw">private</span>:
<span class="type">VkSurfaceKHR</span> surface;

<span class="kw">public</span>:
<span class="cmt">//Getter</span>
<span class="type">VkSurfaceKHR</span> <span class="fn">Surface</span>() <span class="kw">const</span> {
    <span class="kw">return</span> surface;
}

<span class="cmt">//该函数用于选择物理设备前</span>
<span class="kw">void</span> <span class="fn">Surface</span>(<span class="type">VkSurfaceKHR</span> surface) {
    <span class="kw">if</span> (!<span class="kw">this</span>-&gt;surface)
        <span class="kw">this</span>-&gt;surface = surface;
}
</pre>
<ul>
    <li>
        <p>如果你有多个窗口（应用程序可以有子窗口），当然也会有多个window surface，以及多个交换链，本套教程对此不作考虑。</p>
    </li>
</ul></section>
<section id="id3">
<h3>创建逻辑设备的步骤<a class="headerlink" href="#id3" title="Permalink to this heading"></a></h3>
<p>
    创建逻辑设备的步骤依序为：
    <br>
    1.获取物理设备列表
    <br>
    2.检查物理设备是否满足所需的队列族类型，从中选择能满足要求的设备并顺便取得队列族索引
    <br>
    3.确定所需的设备级别扩展，不检查是否可用
    <br>
    4.用<a href="https://renderdoc.org/vkspec_chunked/chap5.html#vkCreateDevice">vkCreateDevice</a>(...)创建逻辑设备，取得队列
    <br>
    5.取得物理设备属性、物理设备内存属性，以备之后使用
</p>
<ul>
    <li>
        <p>
            不检查扩展是否可用的原因同前文不检查实例级别层和扩展是否可用的原因相似，<a href="https://renderdoc.org/vkspec_chunked/chap5.html#vkCreateDevice">vkCreateDevice</a>(...)本就会对扩展是否可用进行检查。
        </p>
    </li>
    <li>
        <p>
            第5步的内容是以创建逻辑设备成功，确定将要使用的物理设备不再变动为前提的，若你对物理设备的性能有要求，也可以在第2步前自行取得各个物理设备的属性，进行甄选。
        </p>
    </li>
</ul>
<p>
    <strong>根据情况，一共需要三种类型的队列：图形、呈现、计算。</strong>
    <br>
    呈现队列并非是Vulkan核心功能中规定的队列类型。几乎可以肯定，GPU必定会有一个同时支持图形和呈现的队列族（该说法来自谷歌的搜索结果，我认为是准确的，考虑到没找到与此相悖的报告），但标准中并没有作此规定，因此保险起见，我将其单独列出来。
</p>
<ul>
    <li>
        <p>
            如果你的程序没有图形界面（比如，仅仅用于对图像做某种处理的控制台程序），那么呈现队列非必须。
        </p>
    </li>
    <li>
        <p>
            如果你不需要GPU计算或间接渲染（将CPU上的一些计算扔到GPU上然后再从计算结果做渲染），那么计算队列非必须。
        </p>
    </li>
    <li>
        <p>
            如果你只打算搞计算（GPU是高度并行的计算设备）而不搞渲染，那么图形队列非必须。</p>
        </p>
    </li>
</ul>
<p>
    于是在<span class="type">graphicsBase</span>中加入以下内容：
</p>
<pre class="code">
<span class="kw">private</span>:
<span class="type">VkPhysicalDevice</span> physicalDevice;
<span class="type">VkPhysicalDeviceProperties</span> physicalDeviceProperties;
<span class="type">VkPhysicalDeviceMemoryProperties</span> physicalDeviceMemoryProperties;
std::<span class="type">vector</span>&lt;<span class="type">VkPhysicalDevice</span>&gt; availablePhysicalDevices;

<span class="type">VkDevice</span> device;
<span class="cmt">//有效的索引从0开始，因此使用特殊值VK_QUEUE_FAMILY_IGNORED（为UINT32_MAX）为队列族索引的默认值</span>
<span class="type">uint32_t</span> queueFamilyIndex_graphics = <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>;
<span class="type">uint32_t</span> queueFamilyIndex_presentation = <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>;
<span class="type">uint32_t</span> queueFamilyIndex_compute = <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>;
<span class="type">VkQueue</span> queue_graphics;
<span class="type">VkQueue</span> queue_presentation;
<span class="type">VkQueue</span> queue_compute;

std::<span class="type">vector</span>&lt;<span class="kw">const char</span>*&gt; deviceExtensions;

<span class="cmt">//该函数被DeterminePhysicalDevice(...)调用，用于检查物理设备是否满足所需的队列族类型，并将对应的队列族索引返回到queueFamilyIndices，执行成功时直接将索引写入相应成员变量</span>
<span class="type">VkResult</span> <span class="fn">GetQueueFamilyIndices</span>(<span class="type">VkPhysicalDevice</span> physicalDevice, <span class="kw">bool</span> enableGraphicsQueue, <span class="kw">bool</span> enableComputeQueue, <span class="type">uint32_t</span> (&queueFamilyIndices)[3]) {
    <span class="cmt">/*待Ch1-3填充*/</span>
}

<span class="kw">public</span>:
<span class="cmt">//Getter</span>
<span class="type">VkPhysicalDevice</span> <span class="fn">PhysicalDevice</span>() <span class="kw">const</span> {
    <span class="kw">return</span> physicalDevice;
}
<span class="kw">const</span> <span class="type">VkPhysicalDeviceProperties</span>&amp; <span class="fn">PhysicalDeviceProperties</span>() <span class="kw">const</span> {
    <span class="kw">return</span> physicalDeviceProperties;
}
<span class="kw">const</span> <span class="type">VkPhysicalDeviceMemoryProperties</span>&amp; <span class="fn">PhysicalDeviceMemoryProperties</span>() <span class="kw">const</span> {
    <span class="kw">return</span> physicalDeviceMemoryProperties;
}
<span class="type">VkPhysicalDevice</span> <span class="fn">AvailablePhysicalDevice</span>(<span class="type">uint32_t</span> index) <span class="kw">const</span> {
    <span class="kw">return</span> availablePhysicalDevices[index];
}
<span class="type">uint32_t</span> <span class="fn">AvailablePhysicalDeviceCount</span>() <span class="kw">const</span> {
    <span class="kw">return</span> <span class="type">uint32_t</span>(availablePhysicalDevices.<span class="fn">size()</span>);
}

<span class="type">VkDevice</span> <span class="fn">Device</span>() <span class="kw">const</span> {
    <span class="kw">return</span> device;
}
<span class="type">uint32_t</span> <span class="fn">QueueFamilyIndex_Graphics</span>() <span class="kw">const</span> {
    <span class="kw">return</span> queueFamilyIndex_graphics;
}
<span class="type">uint32_t</span> <span class="fn">QueueFamilyIndex_Presentation</span>() <span class="kw">const</span> {
    <span class="kw">return</span> queueFamilyIndex_presentation;
}
<span class="type">uint32_t</span> <span class="fn">QueueFamilyIndex_Compute</span>() <span class="kw">const</span> {
    <span class="kw">return</span> queueFamilyIndex_compute;
}
<span class="type">VkQueue</span> <span class="fn">Queue_Graphics</span>() <span class="kw">const</span> {
    <span class="kw">return</span> queue_graphics;
}
<span class="type">VkQueue</span> <span class="fn">Queue_Presentation</span>() <span class="kw">const</span> {
    <span class="kw">return</span> queue_presentation;
}
<span class="type">VkQueue</span> <span class="fn">Queue_Compute</span>() <span class="kw">const</span> {
    <span class="kw">return</span> queue_compute;
}

<span class="kw">const</span> std::<span class="type">vector</span>&lt;<span class="kw">const char</span>*&gt;&amp; <span class="fn">DeviceExtensions()</span> <span class="kw">const</span> {
    <span class="kw">return</span> deviceExtensions;
}

<span class="cmt">//该函数用于创建逻辑设备前</span>
<span class="kw">void</span> <span class="fn">AddDeviceExtension</span>(<span class="kw">const char</span>* extensionName) {
    <span class="sfn">AddLayerOrExtension</span>(deviceExtensions, extensionName);
}
<span class="cmt">//该函数用于获取物理设备</span>
<span class="type">VkResult</span> <span class="fn">GetPhysicalDevices</span>() {
    <span class="cmt">/*待Ch1-3填充*/</span>
}
<span class="cmt">//该函数用于指定所用物理设备并调用GetQueueFamilyIndices(...)取得队列族索引</span>
<span class="type">VkResult</span> <span class="fn">DeterminePhysicalDevice</span>(<span class="type">uint32_t</span> deviceIndex = 0, <span class="kw">bool</span> enableGraphicsQueue, <span class="kw">bool</span> enableComputeQueue = <span class="kw">true</span>) {
    <span class="cmt">/*待Ch1-3填充*/</span>
}
<span class="cmt">//该函数用于创建逻辑设备，并取得队列</span>
<span class="type">VkResult</span> <span class="fn">CreateDevice</span>(<span class="type">VkDeviceCreateFlags</span> flags = 0) {
    <span class="cmt">/*待Ch1-3填充*/</span>
}
<span class="cmt">//以下函数用于创建逻辑设备失败后</span>
<span class="type">VkResult</span> <span class="fn">CheckDeviceExtensions</span>(std::<span class="type">span</span>&lt;<span class="kw">const char</span>*&gt; extensionsToCheck, <span class="kw">const char</span>* layerName = <span class="kw">nullptr</span>) <span class="kw">const</span> {
    <span class="cmt">/*待Ch1-3填充*/</span>
}
<span class="kw">void</span> <span class="fn">DeviceExtensions</span>(<span class="kw">const</span> std::<span class="type">vector</span>&lt;<span class="kw">const char</span>*&gt;&amp; extensionNames) {
    deviceExtensions = extensionNames;
}
</pre>
<ul>
    <li>
        <p>
            检查硬件是否支持光追管线的方式与检查是否支持图形和计算管线的方式不同，这里暂不作考虑。
        </p>
    </li>
</ul>
<p>
    <strong>于是剧本流程依序是这样的：</strong>
    <br>
    1.用<span class="fn">GetPhysicalDevices</span>()获取可用物理设备的列表。
    <br>
    2.然后，用<span class="fn">DeterminePhysicalDevice</span>(...)判断将要使用的物理设备是否满足要求，如满足则将其记录为所用设备。这一步需要判断物理设备是否支持window surface，正是因此创建window surface一步须在此之前。
    <br>
    3.然后，用<span class="fn">AddDeviceExtension</span>(...)向<span class="var">deviceExtensions</span>中添加指向各扩展名称的指针。
    <br>
    4.然后尝试用<span class="fn">CreateDevice</span>(...)创建逻辑设备，若创建成功，该函数会取得队列。
    <br>
    5.若创建逻辑设备失败，尝试用<span class="fn">DeterminePhysicalDevice</span>(...)更换物理设备。到这一步为止仍没有检查扩展，如果你首先选用的是性能较好的物理设备，且优先看重性能（而非支持的扩展），那么这一步与下一步可互换。
    <br>
    6.若遍历所有物理设备后仍无法创建逻辑设备且<a href="https://renderdoc.org/vkspec_chunked/chap5.html#vkCreateDevice">vkCreateDevice</a>(...)返回<span class="enum">VK_ERROR_EXTENSION_NOT_PRESENT</span>，从<span class="fn">DeviceExtensions</span>()复制一份<span class="var">deviceExtensions</span>，嵌套循环中用<span class="fn">DeterminePhysicalDevice</span>(...)遍历物理设备（会有代码防止反复取得队列族索引），用<span class="fn">CheckDeviceExtensions</span>(...)检查扩展可用性，对于某物理设备，若不可用的仅为非必要的扩展（不过为应对没有某个扩展而书写两套代码可能会很麻烦），创建一份去除该扩展后的<span class="type">vector</span>，用<span class="type">DeviceExtensions</span>(...)复制给<span class="var">deviceExtensions</span>。然后重新尝试创建逻辑设备。
</p>
<p>
    上述流程优先考虑在最佳情况下获得最快的初始化速度，因而显得有些繁琐（最佳情况下不需要检查扩展，最糟糕的情况为遍历所有物理设备一次后，因全数无法满足所需扩展，再次遍历并对每个设备检查扩展可用性），如果你的应用程序用到了显卡未必支持但对于程序运行非必要的扩展，且程序对显卡性能没有要求，可以先检查扩展（最佳情况下需要检查扩展一次，最糟糕的情况下需要遍历所有物理设备一次并对每个设备检查扩展可用性一次）。
</p></section>
<section id="id4">
<h3>创建交换链的步骤<a class="headerlink" href="#id4" title="Permalink to this heading"></a></h3>
<p>
    创建交换链的步骤依序为：
    <br>
    1.填写一大堆信息
    <br>
    2.创建交换链并取得交换链图像，为交换链图像创建image view
</p>
<p>
    <strong>什么是image view？</strong>
    <br>
    Vulkan中，<span class="type">VkImage</span>引用一片<a class="reference internal" href="Ch3-2%20%E5%9B%BE%E5%83%8F%E4%B8%8E%E7%BC%93%E5%86%B2%E5%8C%BA.html#device-memory">物理设备内存</a>，将该片内存上的数据用作图像，而<span class="type">VkImageView</span>指定图像的使用方式，比如，一个6层的二维图像，可以用作6层的二维图像数组，也可用作天空盒。
</p>
<p>
    书写创建交换链的函数时，重要的一点是要考虑重建交换链，于是在<span class="type">graphicsBase</span>中加入以下内容：
</p>
<pre class="code">
<span class="kw">private</span>:
std::<span class="type">vector</span> &lt;<span class="type">VkSurfaceFormatKHR</span>&gt; availableSurfaceFormats;

<span class="type">VkSwapchainKHR</span> swapchain;
std::<span class="type">vector</span> &lt;<span class="type">VkImage&gt;</span> swapchainImages;
std::<span class="type">vector</span> &lt;<span class="type">VkImageView&gt;</span> swapchainImageViews;
<span class="cmt">//保存交换链的创建信息以便重建交换链</span>
<span class="type">VkSwapchainCreateInfoKHR</span> swapchainCreateInfo = {};

<span class="cmt">//该函数被CreateSwapchain(...)和RecreateSwapchain()调用</span>
<span class="type">VkResult</span> <span class="fn">CreateSwapchain_Internal</span>() {
    <span class="cmt">/*待Ch1-4填充*/</span>
}

<span class="kw">public</span>:
<span class="cmt">//Getter</span>
<span class="kw">const</span> <span class="type">VkFormat</span>&amp; <span class="fn">AvailableSurfaceFormat</span>(<span class="type">uint32_t</span> index) <span class="kw">const</span> {
    <span class="kw">return</span> availableSurfaceFormats[index].format;
}
<span class="kw">const</span> <span class="type">VkColorSpaceKHR</span>&amp; <span class="fn">AvailableSurfaceColorSpace</span>(<span class="type">uint32_t</span> index) <span class="kw">const</span> {
    <span class="kw">return</span> availableSurfaceFormats[index].colorSpace;
}
<span class="type">uint32_t</span> <span class="fn">AvailableSurfaceFormatCount</span>() <span class="kw">const</span> {
    <span class="kw">return</span> <span class="type">uint32_t</span>(availableSurfaceFormats.<span class="fn">size</span>());
}

<span class="type">VkSwapchainKHR</span> <span class="fn">Swapchain</span>() <span class="kw">const</span> {
    <span class="kw">return</span> swapchain;
}
<span class="type">VkImage</span> <span class="fn">SwapchainImage</span>(<span class="type">uint32_t</span> index) <span class="kw">const</span> {
    <span class="kw">return</span> swapchainImages[index];
}
<span class="type">VkImageView</span> <span class="fn">SwapchainImageView</span>(<span class="type">uint32_t</span> index) <span class="kw">const</span> {
    <span class="kw">return</span> swapchainImageViews[index];
}
<span class="type">uint32_t</span> <span class="fn">SwapchainImageCount</span>() <span class="kw">const</span> {
    <span class="kw">return</span> <span class="type">uint32_t</span>(swapchainImages.<span class="fn">size</span>());
}
<span class="kw">const</span> <span class="type">VkSwapchainCreateInfoKHR</span>&amp; <span class="fn">SwapchainCreateInfo</span>() <span class="kw">const</span> {
    <span class="kw">return</span> swapchainCreateInfo;
}

<span class="type">VkResult</span> <span class="fn">GetSurfaceFormats</span>() {
    <span class="cmt">/*待Ch1-4填充*/</span>
}
<span class="type">VkResult</span> <span class="fn">SetSurfaceFormat</span>(<span class="type">VkSurfaceFormatKHR</span> surfaceFormat) {
    <span class="cmt">/*待Ch1-4填充*/</span>
}
<span class="cmt">//该函数用于创建交换链</span>
<span class="type">VkResult</span> <span class="fn">CreateSwapchain</span>(<span class="kw">bool</span> limitFrameRate = <span class="kw">true</span>, <span class="type">VkSwapchainCreateFlagsKHR</span> flags = 0) {
    <span class="cmt">/*待Ch1-4填充*/</span>
}
<span class="cmt">//该函数用于重建交换链</span>
<span class="type">VkResult</span> <span class="fn">RecreateSwapchain</span>() {
    <span class="cmt">/*待Ch1-4填充*/</span>
}
</pre>
<ul>
    <li>
        <p>
            我定义<span class="fn">SetSurfaceFormat</span>(...)意在指定HDR这类特殊的色彩空间，在此之前需手动调用<span class="fn">GetSurfaceFormats</span>()来取得surface的可用格式。
        </p>
    </li>
    <li>
        <p>
            若因为不需要指定surface格式或色彩空间而没有手动调用<span class="fn">GetSurfaceFormats</span>()，让<span class="fn">CreateSwapchain</span>(...)将调用它。
        </p>
    </li>
</ul>
<p>
    <strong>于是剧本流程依序是这样的：</strong>
    <br>
    1.若需要指定交换链的图像格式和色彩空间，用<span class="fn">GetSurfaceFormats</span>()来取得surface的可用格式到<span class="var">availableSurfaceFormats</span>，用<span class="fn">SetSurfaceFormat</span>(...)验证并指定图像格式和色彩空间。
    <br>
    2.用<span class="fn">CreateSwapchain</span>(...)创建交换链。
    <br>
    3.在之后的运行中，窗口大小改变时（可通过GLFW的回调函数，或WindowsAPI中的message得知），调用<span class="fn">RecreateSwapchain</span>()。
</p></section>
</section>
<section id="id5">
<h2>若要使用最新版Vulkan<a class="headerlink" href="#id5" title="Permalink to this heading"></a></h2>
<p>
    如果你想使用Vulkan的最新版本，须在创建Vulkan实例前，取得当前运行环境所支持的最新Vulkan版本，在<span class="type">graphicsBase</span>中加入以下内容：
</p>
<pre class="code">
<span class="kw">private</span>:
<span class="type">uint32_t</span> apiVersion = <span class="mcr">VK_API_VERSION_1_0</span>;

<span class="kw">public</span>:
<span class="cmt">//Getter</span>
<span class="type">uint32_t</span> <span class="fn">ApiVersion</span>() <span class="kw">const</span> {
    <span class="kw">return</span> apiVersion;
}
<span class="type">VkResult</span> <span class="fn">UseLatestApiVersion</span>() {
    <span class="cmt">/*待Ch1-3填充*/</span>
}
</pre></section>
<section id="id6">
<h2>代码汇总<a class="headerlink" href="#id6" title="Permalink to this heading"></a></h2>
<p>
    本节中总共定义了如下内容：
</p>
<pre class="code">
<span class="kw">class</span> <span class="type">graphicsBase</span> {
    <span class="type">uint32_t</span> apiVersion = <span class="mcr">VK_API_VERSION_1_0</span>;
    <span class="type">VkInstance</span> instance;
    <span class="type">VkPhysicalDevice</span> physicalDevice;
    <span class="type">VkPhysicalDeviceProperties</span> physicalDeviceProperties;
    <span class="type">VkPhysicalDeviceMemoryProperties</span> physicalDeviceMemoryProperties;
    std::<span class="type">vector</span>&lt;<span class="type">VkPhysicalDevice</span>&gt; availablePhysicalDevices;

    <span class="type">VkDevice</span> device;
    <span class="type">uint32_t</span> queueFamilyIndex_graphics = <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>;
    <span class="type">uint32_t</span> queueFamilyIndex_presentation = <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>;
    <span class="type">uint32_t</span> queueFamilyIndex_compute = <span class="mcr">VK_QUEUE_FAMILY_IGNORED</span>;
    <span class="type">VkQueue</span> queue_graphics;
    <span class="type">VkQueue</span> queue_presentation;
    <span class="type">VkQueue</span> queue_compute;

    <span class="type">VkSurfaceKHR</span> surface;
    std::<span class="type">vector</span> &lt;<span class="type">VkSurfaceFormatKHR</span>&gt; availableSurfaceFormats;

    <span class="type">VkSwapchainKHR</span> swapchain;
    std::<span class="type">vector</span> &lt;<span class="type">VkImage&gt;</span> swapchainImages;
    std::<span class="type">vector</span> &lt;<span class="type">VkImageView&gt;</span> swapchainImageViews;
    <span class="type">VkSwapchainCreateInfoKHR</span> swapchainCreateInfo = {};


    std::<span class="type">vector</span>&lt;<span class="kw">const char</span>*&gt; instanceLayers;
    std::<span class="type">vector</span>&lt;<span class="kw">const char</span>*&gt; instanceExtensions;
    std::<span class="type">vector</span>&lt;<span class="kw">const char</span>*&gt; deviceExtensions;

    <span class="type">VkDebugUtilsMessengerEXT</span> debugMessenger;

    <span class="cmt">//静态变量</span>
    <span class="kw">static</span> <span class="type">graphicsBase</span> singleton;
    <span class="cmt">//--------------------</span>
    <span class="fn">graphicsBase</span>() = <span class="kw">default</span>;
    <span class="fn">graphicsBase</span>(<span class="type">graphicsBase</span>&amp;&amp;) = <span class="kw">delete</span>;
    <span class="fn">~graphicsBase</span>() {
        <span class="cmt">/*待Ch1-4填充*/</span>
    }
    <span class="cmt">Non-const函数</span>
    <span class="type">VkResult</span> <span class="fn">CreateSwapchain_Internal</span>() {
        <span class="cmt">/*待Ch1-4填充*/</span>
    }
    <span class="type">VkResult</span> <span class="fn">GetQueueFamilyIndices</span>(<span class="type">VkPhysicalDevice</span> physicalDevice, <span class="kw">bool</span> enableGraphicsQueue, <span class="kw">bool</span> enableComputeQueue, <span class="type">uint32_t</span> (&queueFamilyIndices)[3]) {
        <span class="cmt">/*待Ch1-3填充*/</span>
    }

    <span class="type">VkResult</span> <span class="fn">CreateDebugMessenger</span>() {
        <span class="cmt">/*待Ch1-3填充*/</span>
    }
<span class="kw">public</span>:
    <span class="cmt">//Getter</span>
    <span class="type">uint32_t</span> <span class="fn">ApiVersion</span>() <span class="kw">const</span> {
        <span class="kw">return</span> apiVersion;
    }
    <span class="type">VkInstance</span> <span class="fn">Instance</span>() <span class="kw">const</span> {
        <span class="kw">return</span> instance;
    }
    <span class="type">VkPhysicalDevice</span> <span class="fn">PhysicalDevice</span>() <span class="kw">const</span> {
        <span class="kw">return</span> physicalDevice;
    }
    <span class="kw">const</span> <span class="type">VkPhysicalDeviceProperties</span>&amp; <span class="fn">PhysicalDeviceProperties</span>() <span class="kw">const</span> {
        <span class="kw">return</span> physicalDeviceProperties;
    }
    <span class="kw">const</span> <span class="type">VkPhysicalDeviceMemoryProperties</span>&amp; <span class="fn">PhysicalDeviceMemoryProperties</span>() <span class="kw">const</span> {
        <span class="kw">return</span> physicalDeviceMemoryProperties;
    }
    <span class="type">VkPhysicalDevice</span> <span class="fn">AvailablePhysicalDevice</span>(<span class="type">uint32_t</span> index) <span class="kw">const</span> {
        <span class="kw">return</span> availablePhysicalDevices[index];
    }
    <span class="type">uint32_t</span> <span class="fn">AvailablePhysicalDeviceCount</span>() <span class="kw">const</span> {
        <span class="kw">return</span> <span class="type">uint32_t</span>(availablePhysicalDevices.<span class="fn">size()</span>);
    }

    <span class="type">VkDevice</span> <span class="fn">Device</span>() <span class="kw">const</span> {
        <span class="kw">return</span> device;
    }
    <span class="type">uint32_t</span> <span class="fn">QueueFamilyIndex_Graphics</span>() <span class="kw">const</span> {
        <span class="kw">return</span> queueFamilyIndex_graphics;
    }
    <span class="type">uint32_t</span> <span class="fn">QueueFamilyIndex_Presentation</span>() <span class="kw">const</span> {
        <span class="kw">return</span> queueFamilyIndex_presentation;
    }
    <span class="type">uint32_t</span> <span class="fn">QueueFamilyIndex_Compute</span>() <span class="kw">const</span> {
        <span class="kw">return</span> queueFamilyIndex_compute;
    }
    <span class="type">VkQueue</span> <span class="fn">Queue_Graphics</span>() <span class="kw">const</span> {
        <span class="kw">return</span> queue_graphics;
    }
    <span class="type">VkQueue</span> <span class="fn">Queue_Presentation</span>() <span class="kw">const</span> {
        <span class="kw">return</span> queue_presentation;
    }
    <span class="type">VkQueue</span> <span class="fn">Queue_Compute</span>() <span class="kw">const</span> {
        <span class="kw">return</span> queue_compute;
    }

    <span class="type">VkSurfaceKHR</span> <span class="fn">Surface</span>() <span class="kw">const</span> {
        <span class="kw">return</span> surface;
    }
    <span class="kw">const</span> <span class="type">VkFormat</span>&amp; <span class="fn">AvailableSurfaceFormat</span>(<span class="type">uint32_t</span> index) <span class="kw">const</span> {
        <span class="kw">return</span> availableSurfaceFormats[index].format;
    }
    <span class="kw">const</span> <span class="type">VkColorSpaceKHR</span>&amp; <span class="fn">AvailableSurfaceColorSpace</span>(<span class="type">uint32_t</span> index) <span class="kw">const</span> {
        <span class="kw">return</span> availableSurfaceFormats[index].colorSpace;
    }
    <span class="type">uint32_t</span> <span class="fn">AvailableSurfaceFormatCount</span>() <span class="kw">const</span> {
        <span class="kw">return</span> <span class="type">uint32_t</span>(availableSurfaceFormats.<span class="fn">size</span>());
    }

    <span class="type">VkSwapchainKHR</span> <span class="fn">Swapchain</span>() <span class="kw">const</span> {
        <span class="kw">return</span> swapchain;
    }
    <span class="type">VkImage</span> <span class="fn">SwapchainImage</span>(<span class="type">uint32_t</span> index) <span class="kw">const</span> {
        <span class="kw">return</span> swapchainImages[index];
    }
    <span class="type">VkImageView</span> <span class="fn">SwapchainImageView</span>(<span class="type">uint32_t</span> index) <span class="kw">const</span> {
        <span class="kw">return</span> swapchainImageViews[index];
    }
    <span class="type">uint32_t</span> <span class="fn">SwapchainImageCount</span>() <span class="kw">const</span> {
        <span class="kw">return</span> <span class="type">uint32_t</span>(swapchainImages.<span class="fn">size</span>());
    }
    <span class="kw">const</span> <span class="type">VkSwapchainCreateInfoKHR</span>&amp; <span class="fn">SwapchainCreateInfo</span>() <span class="kw">const</span> {
        <span class="kw">return</span> swapchainCreateInfo;
    }

    <span class="kw">const</span> std::<span class="type">vector</span>&lt;<span class="kw">const char</span>*&gt;&amp; <span class="fn">InstanceLayers()</span> <span class="kw">const</span> {
        <span class="kw">return</span> instanceLayers;
    }
    <span class="kw">const</span> std::<span class="type">vector</span>&lt;<span class="kw">const char</span>*&gt;&amp; <span class="fn">InstanceExtensions()</span> <span class="kw">const</span> {
        <span class="kw">return</span> instanceExtensions;
    }
    <span class="kw">const</span> std::<span class="type">vector</span>&lt;<span class="kw">const char</span>*&gt;&amp; <span class="fn">DeviceExtensions()</span> <span class="kw">const</span> {
        <span class="kw">return</span> deviceExtensions;
    }

    <span class="cmt">//Const函数</span>
    <span class="type">VkResult</span> <span class="fn">CheckInstanceLayers</span>(std::<span class="type">span</span>&lt;<span class="kw">const char</span>*&gt; layersToCheck) <span class="kw">const</span> {
        <span class="cmt">/*待Ch1-3填充*/</span>
    }
    <span class="type">VkResult</span> <span class="fn">CheckInstanceExtensions</span>(std::<span class="type">span</span>&lt;<span class="kw">const char</span>*&gt; extensionsToCheck, <span class="kw">const char</span>* layerName = <span class="kw">nullptr</span>) <span class="kw">const</span> {
        <span class="cmt">/*待Ch1-3填充*/</span>
    }
    <span class="type">VkResult</span> <span class="fn">CheckDeviceExtensions</span>(std::<span class="type">span</span>&lt;<span class="kw">const char</span>*&gt; extensionsToCheck, <span class="kw">const char</span>* layerName = <span class="kw">nullptr</span>) <span class="kw">const</span> {
        <span class="cmt">/*待Ch1-3填充*/</span>
    }

    <span class="cmt">//Non-const函数</span>
    <span class="kw">void</span> <span class="fn">AddInstanceLayer</span>(<span class="kw">const char</span>* layerName) {
        instanceLayers.<span class="fn">push_back</span>(layerName);
    }
    <span class="kw">void</span> <span class="fn">AddInstanceExtension</span>(<span class="kw">const char</span>* extensionName) {
        instanceExtensions.<span class="fn">push_back</span>(extensionName);
    }
    <span class="kw">void</span> <span class="fn">AddDeviceExtension</span>(<span class="kw">const char</span>* extensionName) {
        deviceExtensions.<span class="fn">push_back</span>(extensionName);
    }

    <span class="type">VkResult</span> <span class="fn">UseLatestApiVersion</span>() {
        <span class="cmt">/*待Ch1-3填充*/</span>
    }
    <span class="type">VkResult</span> <span class="fn">CreateInstance</span>(<span class="type">VkInstanceCreateFlags</span> flags = 0) {
        <span class="cmt">/*待Ch1-3填充*/</span>
    }
    <span class="kw">void</span> <span class="fn">Surface</span>(<span class="type">VkSurfaceKHR</span> surface) {
        <span class="kw">if</span> (!<span class="kw">this</span>-&gt;surface)
            <span class="kw">this</span>-&gt;surface = surface;
    }
    <span class="type">VkResult</span> <span class="fn">GetPhysicalDevices</span>() {
        <span class="cmt">/*待Ch1-3填充*/</span>
    }
    <span class="type">VkResult</span> <span class="fn">DeterminePhysicalDevice</span>(<span class="type">uint32_t</span> deviceIndex = 0, <span class="kw">bool</span> enableGraphicsQueue, <span class="kw">bool</span> enableComputeQueue = <span class="kw">true</span>) {
        <span class="cmt">/*待Ch1-3填充*/</span>
    }
    <span class="type">VkResult</span> <span class="fn">CreateDevice</span>(<span class="type">VkDeviceCreateFlags</span> flags = 0) {
        <span class="cmt">/*待Ch1-3填充*/</span>
    }
    <span class="type">VkResult</span> <span class="fn">GetSurfaceFormats</span>() {
        <span class="cmt">/*待Ch1-4填充*/</span>
    }
    <span class="type">VkResult</span> <span class="fn">SetSurfaceFormat</span>(<span class="type">VkSurfaceFormatKHR</span> surfaceFormat) {
        <span class="cmt">/*待Ch1-4填充*/</span>
    }
    <span class="type">VkResult</span> <span class="fn">CreateSwapchain</span>(<span class="kw">bool</span> limitFrameRate = <span class="kw">true</span>, <span class="type">VkSwapchainCreateFlagsKHR</span> flags = 0) {
        <span class="cmt">/*待Ch1-4填充*/</span>
    }

    <span class="kw">void</span> <span class="fn">InstanceLayers</span>(<span class="kw">const</span> std::<span class="type">vector</span>&lt;<span class="kw">const char</span>*&gt;&amp; layerNames) {
        instanceLayers = layerNames;
    }
    <span class="kw">void</span> <span class="fn">InstanceExtensions</span>(<span class="kw">const</span> std::<span class="type">vector</span>&lt;<span class="kw">const char</span>*&gt;&amp; extensionNames) {
        instanceExtensions = extensionNames;
    }
    <span class="kw">void</span> <span class="fn">DeviceExtensions</span>(<span class="kw">const</span> std::<span class="type">vector</span>&lt;<span class="kw">const char</span>*&gt;&amp; extensionNames) {
        deviceExtensions = extensionNames;
    }

    <span class="type">VkResult</span> <span class="fn">RecreateSwapchain</span>() {
        <span class="cmt">/*待Ch1-4填充*/</span>
    }
    <span class="cmt">//静态函数</span>
    <span class="kw">static</span> <span class="type">graphicsBase</span>&amp; <span class="sfn">Base</span>() {
        <span class="kw">return</span> singleton;
    }
};
<span class="kw">inline</span> <span class="type">graphicsBase</span> <span class="type">graphicsBase</span>::singleton;
</pre></section>
</section>


           </div>
          </div>
          <footer><div class="rst-footer-buttons" role="navigation" aria-label="Footer">
        <a href="Ch1-1%20%E5%88%9B%E5%BB%BAGLFW%E7%AA%97%E5%8F%A3.html" class="btn btn-neutral float-left" title="Ch1-1 创建GLFW窗口" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left" aria-hidden="true"></span> 上一页</a>
        <a href="Ch1-3%20%E5%88%9B%E5%BB%BAVK%E5%AE%9E%E4%BE%8B%E4%B8%8E%E9%80%BB%E8%BE%91%E8%AE%BE%E5%A4%87.html" class="btn btn-neutral float-right" title="Ch1-3 创建VK实例与逻辑设备" accesskey="n" rel="next">下一页 <span class="fa fa-arrow-circle-right" aria-hidden="true"></span></a>
    </div>

  <hr/>

  <div role="contentinfo">
    <p>&#169; 版权所有 2021-2024, Qiao YeCheng.</p>
  </div>

  利用 <a href="https://www.sphinx-doc.org/">Sphinx</a> 构建，使用了 
    <a href="https://github.com/readthedocs/sphinx_rtd_theme">主题</a>
    由 <a href="https://readthedocs.org">Read the Docs</a>开发.
   

</footer>
        </div>
      </div>
    </section>
  </div>
  <script>
      jQuery(function () {
          SphinxRtdTheme.Navigation.enable(true);
      });
  </script> 

</body>
</html>